# v0.24.0

# 功能测试

## uds 热升级

**注意**

mosn热升级需要使用UDS(unix domain socket)

### 编写sofa rpc client

```go
// ./client.go
package main

import (
	"context"
	"flag"
	"fmt"
	"net"
	"time"

	"mosn.io/api"

	"mosn.io/mosn/pkg/log"
	"mosn.io/mosn/pkg/network"
	"mosn.io/mosn/pkg/protocol"
	"mosn.io/mosn/pkg/protocol/xprotocol/bolt"
	"mosn.io/mosn/pkg/stream"
	_ "mosn.io/mosn/pkg/stream/xprotocol"
	"mosn.io/mosn/pkg/types"
)

type Client struct {
	proto  types.ProtocolName
	Client stream.Client
	conn   types.ClientConnection
	Id     uint64
}

func NewClient(addr string, proto types.ProtocolName) *Client {
	c := &Client{}
	stopChan := make(chan struct{})
	remoteAddr, _ := net.ResolveUnixAddr("unix", addr)
	conn := network.NewClientConnection(0, nil, remoteAddr, stopChan)
	if err := conn.Connect(); err != nil {
		fmt.Println(err)
		return nil
	}
	// pass sub protocol to stream client
	ctx := context.WithValue(context.Background(), types.ContextSubProtocol, string(proto))
	c.Client = stream.NewStreamClient(ctx, protocol.Xprotocol, conn, nil)
	c.conn = conn
	c.proto = proto
	return c
}

func (c *Client) OnReceive(ctx context.Context, headers types.HeaderMap, data types.IoBuffer, trailers types.HeaderMap) {
	fmt.Printf("[Xprotocol RPC Client] Receive Data:")
	if cmd, ok := headers.(api.XFrame); ok {
		streamID := protocol.StreamIDConv(cmd.GetRequestId())

		if resp, ok := cmd.(api.XRespFrame); ok {
			fmt.Println("stream:", streamID, " status:", resp.GetStatusCode())
		}
	}
}

func (c *Client) OnDecodeError(context context.Context, err error, headers types.HeaderMap) {}

func (c *Client) Request() {
	c.Id++
	requestEncoder := c.Client.NewStream(context.Background(), c)

	var request api.XFrame
	switch c.proto {
	case bolt.ProtocolName:
		request = bolt.NewRpcRequest(uint32(c.Id), protocol.CommonHeader(map[string]string{"service": "testSofa"}), nil)
	default:
		panic("unknown protocol, please complete the protocol-switch in Client.Request method")
	}

	requestEncoder.AppendHeaders(context.Background(), request.GetHeader(), true)
}

func main() {
	log.InitDefaultLogger("", log.DEBUG)
	t := flag.Bool("t", false, "-t")
	flag.Parse()
	// use bolt as example
	if client := NewClient("/tmp/test.sock", bolt.ProtocolName); client != nil {
		for {
			client.Request()
			time.Sleep(200 * time.Millisecond)
			if !*t {
				time.Sleep(3 * time.Second)
				return
			}
		}
	}
}
```

### 启动server 和 mosn

mosn配置文件 `./client_config.json`

```json
{
  "close_graceful": false,
  "servers":[
    {
      "default_log_path":"stdout",
      "routers":[
        {
          "router_config_name":"client_router",
          "virtual_hosts":[{
            "name":"clientHost",
            "domains": ["*"],
            "routers": [
              {
                "match":{"headers":[{"name":"service","value":".*"}]},
                "route":{"cluster_name":"clientCluster"}
              }
            ]
          }]
        }
      ],
      "listeners":[
        {
          "name":"clientListener",
          "address": "/tmp/test.sock",
          "network":"unix",
          "bind_port": true,
          "filter_chains": [{
            "filters": [
              {
                "type": "proxy",
                "config": {
                  "downstream_protocol": "X",
                  "upstream_protocol": "X",
                  "extend_config": {
                    "sub_protocol": "bolt"
                  },
                  "router_config_name":"client_router"
                }
              }
            ]
          }]
        }
      ]
    }
  ],
  "cluster_manager":{
    "clusters":[
      {
        "name": "clientCluster",
        "type": "SIMPLE",
        "lb_type": "LB_RANDOM",
        "max_request_per_conn": 1024,
        "conn_buffer_limit_bytes":32768,
        "hosts":[
          {"address":"127.0.0.1:8080"}
        ]
      }
    ]
  },
  "admin": {
    "address": {
      "socket_address": {
        "address": "0.0.0.0",
        "port_value": 34901
      }
    }
  }
}
```

启动server

```bash
$ go run ./examples/codes/sofarpc-with-xprotocol-sample/server.go # 使用mosn example中自带的server
```

启动mosn

```bash
$  go run ./cmd/mosn/... -c  ./client_config.json
2021-08-05 15:26:58,942 [INFO] register a new handler maker, name is default, is default: true
2021-08-05 15:26:58,943 [INFO] [config] processor added to configParsedCBMaps
2021-08-05 15:26:58,950 [INFO] [network] [ register pool factory] register protocol: Http1 factory
2021-08-05 15:26:58,950 [INFO] [network] [ register pool factory] register protocol: Http2 factory
2021-08-05 15:26:58,950 [INFO] [network] [ register pool factory] register protocol: X factory
2021-08-05 15:26:58,954 [INFO] [mosn] [start] xds service type must be sidecar or router
2021-08-05 15:26:58,954 [INFO] load config from :  ./client_config.json
2021-08-05 15:26:58,954 [INFO] mosn parameters parsed cost: 876.549µs
2021-08-05 15:26:58,955 [INFO] [mosn] [init tracing] disable tracing
2021-08-05 15:26:58,955 [INFO] [mosn start] create a new mosn structure
2021-08-05 15:26:58,956 [INFO] [mosn] [NewMosn] new mosn created
2021-08-05 15:26:58,956 [INFO] [mosn start] mosn init cluster structures
2021-08-05 15:26:58,956 [INFO] [cluster] [cluster manager] [AddOrUpdatePrimaryCluster] cluster clientCluster updated
2021-08-05 15:26:58,956 [INFO] [upstream] [host set] update host, final host total: 1
2021-08-05 15:26:58,956 [INFO] [cluster] [primaryCluster] [UpdateHosts] cluster clientCluster update hosts: 1
2021-08-05 15:26:58,956 [INFO] [mosn start] mosn init server structures
2021-08-05 15:26:58,956 [INFO] parsing listen config:unix
2021-08-05 15:26:58,956 [WARN] [streamfilter] createStreamFilterFactoryFromConfig return nil factories
2021-08-05 15:26:58,956 [INFO] [streamfilter] AddOrUpdateStreamFilterConfig add filter chain key: clientListener
2021-08-05 15:26:58,956 [INFO] [server] [conn handler] [add listener] add listener: /tmp/test.sock
2021-08-05 15:26:58,956 [INFO] [router] [virtualhost] [addRouteBase] add a new route rule
2021-08-05 15:26:58,956 [INFO] [router] [routers_manager] [AddOrUpdateRouters] add router: client_router
2021-08-05 15:26:58,956 [INFO] mosn init cost: 1.588619ms
2021-08-05 15:26:58,956 [INFO] [mosn start] mosn start xds client
2021-08-05 15:26:58,956 [WARN] [feature gate] feature XdsMtlsEnable is not enabled
2021-08-05 15:26:58,956 [WARN] [feature gate] feature PayLoadLimitEnable is not enabled
2021-08-05 15:26:58,956 [WARN] [feature gate] feature MultiTenantMode is not enabled
2021-08-05 15:26:58,956 [WARN] [feature gate] feature auto_config is not enabled
2021-08-05 15:26:58,956 [INFO] [mosn start] mosn parse extend config
2021-08-05 15:26:58,956 [INFO] mosn prepare to start cost: 14.834µs
2021-08-05 15:26:58,956 [INFO] xds client start
2021-08-05 15:26:58,956 [ERROR] StaticResources is null
2021-08-05 15:26:58,956 [WARN] fail to init xds config, skip xds: null point exception
```

启动我们编写的client

```bash
$ go run ./examples/codes/sofarpc-with-xprotocol-sample/client.go -t
2021-08-05 15:17:42,715 [INFO] [network] [ register pool factory] register protocol: X factory
[Xprotocol RPC Client] Receive Data:stream: 1  status: 0
[Xprotocol RPC Client] Receive Data:stream: 2  status: 0
[Xprotocol RPC Client] Receive Data:stream: 3  status: 0
[Xprotocol RPC Client] Receive Data:stream: 4  status: 0
[Xprotocol RPC Client] Receive Data:stream: 5  status: 0
[Xprotocol RPC Client] Receive Data:stream: 6  status: 0
[Xprotocol RPC Client] Receive Data:stream: 7  status: 0
....
```

触发mosn热升级

```bash
$ kill -HUP [mosn pid]
```

然后我们可以观察到mosn日志：

```log
2021-08-05 15:29:03,881 [INFO] [admin store] [add service] add server Mosn Admin Server
2021-08-05 15:29:03,881 [INFO] [mosn start] mosn transfer connections
2021-08-05 15:29:03,881 [INFO] [admin store] [start service] [inheritListener] inherit listener addr: [::]:34901
2021-08-05 15:29:03,881 [INFO] [admin store] [stop service] clear all stored services
2021-08-05 15:29:03,881 [INFO] [admin store] [start service] start service Mosn Admin Server on [::]:34901
2021-08-05 15:29:03,881 [WARN] [admin store] [start service] start serve failed : Mosn Admin Server [::]:34901 http: Server closed
2021-08-05 15:29:03,881 [INFO] [mosn start] mosn clean upgrade datas
2021-08-05 15:29:03,881 [INFO] [mosn start] mosn start server
2021-08-05 15:29:03,881 [INFO] mosn start cost: 375.677µs
2021-08-05 15:29:03,882 [INFO] [network] [transfer] [server] TransferServer start
2021-08-05 15:29:03,883 [INFO] [admin store] [stop service] Mosn Admin Server
2021-08-05 15:29:04,882 [INFO] [server] [reconfigure] reconfigureHandler start
2021-08-05 15:29:06,884 [INFO] [server] StopConnection
2021-08-05 15:29:06,884 [INFO] [network] [listener start] [accept] listener clientListener stop accepting connections by deadline
2021-08-05 15:29:07,21 [INFO] [network] [read loop] transferTime: Wait 31 Second
2021-08-05 15:29:38,903 [INFO] TransferRead dataBuf = 0, tlsBuf = 0
2021-08-05 15:29:38,903 [INFO] [network] [transfer] [server] transfer Accept
2021-08-05 15:29:38,903 [INFO] [network] [transfer] [new conn] transferNewConn dataBuf = 0, tlsBuf = 0
2021-08-05 15:29:38,904 [INFO] [network] [new server connection] NewServerConnection id = 2, buffer = 0
2021-08-05 15:29:38,904 [INFO] [network] [transfer] [new conn] transfer NewConn id: 2
2021-08-05 15:29:38,904 [INFO] [network] [transfer] [read] TransferRead NewConn Id = 2, oldId = 2, 0xc0006e4000, addrass = 
2021-08-05 15:29:38,904 [INFO] [network] TransferWrite begin
2021-08-05 15:29:38,904 [INFO] [network] [transfer] [write] TransferWrite id = 2, dataBuf = 20
2021-08-05 15:29:38,904 [INFO] [network] [transfer] [server] transfer Accept
2021-08-05 15:30:36,894 [INFO] [server] [reconfigure] process 27657 gracefully shutdown //升级完成
```

同时我们也可以观察到我们启动的sofa rpc client请求并为发生请求异常，日志如下：

```log
2021-08-05 15:17:50,763 [INFO] [network] [ register pool factory] register protocol: X factory
[Xprotocol RPC Client] Receive Data:stream: 1  status: 0
[Xprotocol RPC Client] Receive Data:stream: 2  status: 0
[Xprotocol RPC Client] Receive Data:stream: 3  status: 0
[Xprotocol RPC Client] Receive Data:stream: 4  status: 0
[Xprotocol RPC Client] Receive Data:stream: 5  status: 0
[Xprotocol RPC Client] Receive Data:stream: 6  status: 0
.......
[Xprotocol RPC Client] Receive Data:stream: 666  status: 0
[Xprotocol RPC Client] Receive Data:stream: 667  status: 0
[Xprotocol RPC Client] Receive Data:stream: 668  status: 0
[Xprotocol RPC Client] Receive Data:stream: 669  status: 0
^Csignal: interrupt
```



## http 短连接

### 编写短连接http 客户端

```go
package main

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

func main() {
	httpClient := &http.Client{}

	for {
		request, err := http.NewRequest("GET", "http://127.0.0.1:2045/haha", nil)
		request.Close = true
		if err != nil {
			log.Fatalln(err)
		}
		time.Sleep(time.Second * 3)
		response, err := httpClient.Do(request)
		if err != nil {
			log.Fatalln(err)
		}
		fmt.Printf("response status: %v time: %v\n", response.Status, time.Now())
		if response.Body != nil {
			response.Body.Close()
		}
	}
}
```

### 运行

运行http server

```bash
$ go run ./examples/codes/http-sample/server.go #复用mosn自带的server 
```

使用如下配置文件运行mosn

```json
{
	"close_graceful" : true,
	"servers":[
		{
			"default_log_path":"stdout",
			"routers":[
				{
					"router_config_name":"client_router",
					"virtual_hosts":[{
						"name":"clientHost",
						"domains": ["*"],
						"routers": [
							{
								"match":{"prefix":"/"},
								"route":{"cluster_name":"clientCluster"}
							}
						]
					}]

				}
			],
			"listeners":[
				{
					"name":"clientListener",
					"address": "127.0.0.1:2045",
					"bind_port": true,
					"filter_chains": [{
						"filters": [
							{
								"type": "proxy",
								"config": {
									"downstream_protocol": "Http1",
									"upstream_protocol": "Http1",
									"router_config_name":"client_router"
								}
							}
						]
					}]
				}
			]
		}
	],
	"cluster_manager":{
		"clusters":[
			{
				"name": "clientCluster",
				"type": "SIMPLE",
				"lb_type": "LB_RANDOM",
				"max_request_per_conn": 1024,
				"conn_buffer_limit_bytes":32768,
				"hosts":[
					{"address":"127.0.0.1:8080"}
				]
			}
		]
	},
	"admin": {
		"address": {
			"socket_address": {
				"address": "0.0.0.0",
				"port_value": 34901
			}
		}
	}
}
```

```bash
$ go run ./cmd/mosn/... -c  ./client_config.json
```

### 查看tcp连接情况


查看tcp连接,会发现现在已经变成http短连接了

```bash
$ netstat -ant | grep '2045'
tcp4       0      0  127.0.0.1.2045         *.*                    LISTEN
tcp4       0      0  127.0.0.1.2045         127.0.0.1.60847        TIME_WAIT
$ netstat -ant | grep '2045'
tcp4       0      0  127.0.0.1.2045         *.*                    LISTEN
tcp4       0      0  127.0.0.1.2045         127.0.0.1.60847        TIME_WAIT
$ netstat -ant | grep '2045'
tcp4       0      0  127.0.0.1.2045         *.*                    LISTEN
tcp4       0      0  127.0.0.1.2045         127.0.0.1.60847        TIME_WAIT
tcp4       0      0  127.0.0.1.2045         127.0.0.1.60878        TIME_WAIT
tcp4       0      0  127.0.0.1.2045         127.0.0.1.60909        TIME_WAIT
```

## grpc 热升级

### 编写hello world服务端代码

```Go
package grpc_network_filter

import (
	"context"
	"encoding/json"
	"google.golang.org/grpc"
	hellopb "google.golang.org/grpc/examples/helloworld/helloworld"
	mgrpc "mosn.io/mosn/pkg/filter/network/grpc"
)

func init() {
	mgrpc.RegisterServerHandler("hello", NewHelloExampleGrpcServer)
}

// helloServer is used to implement helloworld.GreeterServer.
type helloServer struct {
	hellopb.UnimplementedGreeterServer
}

// SayHello implements helloworld.GreeterServer
func (s *helloServer) SayHello(ctx context.Context, in *hellopb.HelloRequest) (*hellopb.HelloReply, error) {
	return &hellopb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func NewHelloExampleGrpcServer(_ json.RawMessage, options ...grpc.ServerOption) (mgrpc.RegisteredServer, error) {
	s := grpc.NewServer(options...)
	hellopb.RegisterGreeterServer(s, &helloServer{})
	return s, nil
}
``` 

### 模拟客户端代码

```Go
func StartClient(mtls bool) {
        go http.ListenAndServe("0.0.0.0:12345", nil)

        pool := x509.NewCertPool()
        pool.AppendCertsFromPEM([]byte(midCa))
        config := &tls.Config{
                RootCAs:    pool,
                ServerName: "127.0.0.1",
        }
        // add tls certificate
        if mtls {
                ca := GetIntermediateCa()
                cert := ca.CreateCertificatePemJson()
                c, err := tls.X509KeyPair([]byte(cert.Cert), []byte(cert.Key))
                if err != nil {
                        panic(err)
                }
                config.Certificates = []tls.Certificate{c}
        }

        connTotal := 20000

        for i := 0; i < connTotal; i++ {
                conn, err := tls.Dial("tcp", "127.0.0.1:2045", config)
                if err != nil {
                        fmt.Println("dial error: ", err)
                        return

                }
                defer conn.Close()
                go func() {
                        conn.SetReadDeadline(time.Time{})
                        if _, err := conn.Write([]byte("test data")); err != nil {
                                fmt.Println("write error: ", err)
                                return
                        }
                        buf := make([]byte, 10)
                        if _, err := conn.Read(buf); err != nil {
                                fmt.Println("read error: ", err)
                                return
                        }
                }()
        }
        fmt.Printf("connected %d conns\n", connTotal)
        // hang up, makes connection exists
        ch := make(chan struct{})
        <-ch

}
```

### 注册server

然后在mosn主函数中导入一下我们写的这个包，让我们写的server注册到mosn grpc network filter框架中

```json
{
  "servers":[
    {
      "default_log_path":"stdout",
      "default_log_level":"INFO",
      "listeners":[
        {
          "address":"127.0.0.1:2045",
          "bind_port": true,
          "filter_chains": [{
            "filters": [
              {
                "type":"grpc",
                "config": {
                  "server_name":"hello"
                }
              }
            ]
          }]
        }
      ]
    }
  ]
}
```

### 启动 mosn

```bash
$  go run ./cmd/mosn/... -c  ./examples/codes/grpc_network/config.json #  
2021-08-04 20:58:05,928 [INFO] register a new handler maker, name is default, is default: true
2021-08-04 20:58:05,929 [INFO] register a grpc server named: hello, success: true
2021-08-04 20:58:05,929 [INFO] [config] processor added to configParsedCBMaps
2021-08-04 20:58:05,934 [INFO] [network] [ register pool factory] register protocol: Http1 factory
2021-08-04 20:58:05,935 [INFO] [network] [ register pool factory] register protocol: Http2 factory
2021-08-04 20:58:05,935 [INFO] [network] [ register pool factory] register protocol: X factory
2021-08-04 20:58:05,937 [INFO] [mosn] [start] xds service type must be sidecar or router
2021-08-04 20:58:05,937 [INFO] load config from :  ./examples/codes/grpc_network/config.json
2021-08-04 20:58:05,938 [INFO] mosn parameters parsed cost: 444.114µs
2021-08-04 20:58:05,938 [INFO] [mosn] [init tracing] disable tracing
2021-08-04 20:58:05,938 [INFO] [mosn start] create a new mosn structure
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn init cluster structures
2021-08-04 20:58:05,939 [WARN] [config] [parse cluster] No Cluster provided in cluster config
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn init server structures
2021-08-04 20:58:05,939 [INFO] parsing listen config:tcp
2021-08-04 20:58:05,939 [WARN] [streamfilter] createStreamFilterFactoryFromConfig return nil factories
2021-08-04 20:58:05,939 [INFO] [streamfilter] AddOrUpdateStreamFilterConfig add filter chain key: c23a1fe8-47e1-4f64-8896-0394e5ed2c45
2021-08-04 20:58:05,939 [INFO] [server] [conn handler] [add listener] add listener: 127.0.0.1:2045
2021-08-04 20:58:05,939 [INFO] mosn init cost: 1.449957ms
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn start xds client
2021-08-04 20:58:05,939 [WARN] [feature gate] feature XdsMtlsEnable is not enabled
2021-08-04 20:58:05,939 [WARN] [feature gate] feature PayLoadLimitEnable is not enabled
2021-08-04 20:58:05,939 [WARN] [feature gate] feature MultiTenantMode is not enabled
2021-08-04 20:58:05,939 [WARN] [feature gate] feature auto_config is not enabled
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn parse extend config
2021-08-04 20:58:05,939 [INFO] mosn prepare to start cost: 11.396µs
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn transfer connections
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn clean upgrade datas
2021-08-04 20:58:05,939 [INFO] [mosn start] mosn start server
2021-08-04 20:58:05,939 [WARN] no admin config, no admin api served
2021-08-04 20:58:05,939 [INFO] [admin store] [mosn state] state changed to 1
2021-08-04 20:58:05,939 [INFO] xds client start
2021-08-04 20:58:05,939 [ERROR] StaticResources is null
2021-08-04 20:58:05,939 [WARN] fail to init xds config, skip xds: null point exception
2021-08-04 20:58:05,939 [INFO] mosn start cost: 11.207µs
2021-08-04 20:58:06,941 [INFO] [server] [reconfigure] reconfigureHandler start
```

### 编写grpc客户端

参考grpc-go官方代码：https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_client/main.go

```go
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

const (
	address     = "127.0.0.1:2045"
)

func main() {
	for TaskID:=0;TaskID<100;TaskID++{
		go func(id int) {
			conn, err := grpc.Dial(address, grpc.WithInsecure())
			if err != nil {
				log.Fatal(err)
			}
			cli := pb.NewGreeterClient(conn)
			data := 0
			for {
				data += 1
				Task(id, cli, fmt.Sprintf("%d", data))
			}
		}(TaskID)
	}

	time.Sleep(time.Minute * 30)
}

func Task(taskID int, cli pb.GreeterClient, data string) {
	ctx, cancel := context.WithCancel(context.TODO())
	defer cancel()

	_, err := cli.SayHello(
		ctx,
		&pb.HelloRequest{Name: data},
	)
	if err != nil {
		log.Fatal(err)
	}
	time.Sleep(time.Millisecond * 10)
}
```

###启动客户端

```bash
$ go run main.go
```

### 触发热升级

```bash
$ kill -HUP [mosn pid]
```

## 路由配置新增变量配置模式

参考单元测试：

https://github.com/mosn/mosn/blob/master/pkg/router/base_rule_test.go#L963

## 路由virtualhost匹配支持端口匹配模式

参考单元测试：

https://github.com/mosn/mosn/blob/master/pkg/router/routers_impl_test.go#L350

## envoy header_to_metadata filter

### 编写两个http server

```go
// server1
package main

import (
	"fmt"
	"net/http"
)

func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	response := fmt.Sprintf("country: %v\n", "china")
	w.Write([]byte(response))
}

func main() {
	http.HandleFunc("/", ServeHTTP)
	http.ListenAndServe("127.0.0.1:10001", nil)
}
```

```go
// server2
package main

import (
	"fmt"
	"net/http"
)

func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	response := fmt.Sprintf("city: %v\n", "hangzhou")
	w.Write([]byte(response))
}

func main() {
	http.HandleFunc("/", ServeHTTP)
	http.ListenAndServe("127.0.0.1:10002", nil)
}
```

### 启动两个http server

```bash
$ go run ./examples/codes/http-sample/server1.go
$ go run ./examples/codes/http-sample/server2.go
```

### 启动mosn

配置文件:

```json
{
	"servers": [
		{
			"routers": [
				{
					"router_config_name": "server_router",
					"virtual_hosts": [
						{
							"name": "serverHost",
							"domains": [
								"*"
							],
							"routers": [
								{
									"route": {
										"cluster_name": "serverCluster"
									}
								}
							]
						}
					]
				}
			],
			"listeners": [
				{
					"name": "example",
					"address": "0.0.0.0:9999",
					"bind_port": true,
					"filter_chains": [
						{
							"filters": [
								{
									"type": "proxy",
									"config": {
										"downstream_protocol": "Http1",
										"upstream_protocol": "Http1",
										"router_config_name": "server_router"
									}
								}
							]
						}
					],
					"stream_filters": [
						{
							"type": "header_to_metadata",
							"config": {
								"request_rules": [
									{
										"header": "x-country",
										"on_header_present": {
											"key": "country"
										},
										"remove": true
									},
									{
										"header": "x-city",
										"on_header_present": {
											"key": "city"
										}
									}
								]
							}
						}
					]
				}
			]
		}
	],
	"cluster_manager": {
		"clusters": [
			{
				"name": "serverCluster",
				"type": "SIMPLE",
				"lb_type": "LB_ROUNDROBIN",
				"max_request_per_conn": 1024,
				"conn_buffer_limit_bytes": 32768,
				"hosts": [
					{
						"address": "127.0.0.1:10001",
						"metadata": {
							"filter_metadata": {
								"mosn.lb": {
									"country": "china"
								}
							}
						},
						"weight": 100
					},
					{
						"address": "127.0.0.1:10002",
						"metadata": {
							"filter_metadata": {
								"mosn.lb": {
									"city": "hangzhou"
								}
							}
						},
						"weight": 100
					}
				],
				"lb_subset_config": {
					"fall_back_policy": 1,
					"subset_selectors": [
						[
							"country"
						],
						[
							"city"
						]
					]
				}
			}
		]
	}
}
```

```bash
$ go run ./cmd/mosn/... -c  ./examples/codes/client_config.json
```

### 请求测试

```bash
$ curl -v "http://127.0.0.1:9999/haha" -H "x-city:hangzhou"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 9999 (#0)
> GET /haha HTTP/1.1
> Host: 127.0.0.1:9999
> User-Agent: curl/7.64.1
> Accept: */*
> x-city:hangzhou
>
< HTTP/1.1 200 OK
< Date: Wed, 04 Aug 2021 15:07:16 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 15
<
city: hangzhou
* Connection #0 to hos
```

```bash
$ curl -v "http://127.0.0.1:9999/haha" -H "x-country:china"
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 9999 (#0)
> GET /haha HTTP/1.1
> Host: 127.0.0.1:9999
> User-Agent: curl/7.64.1
> Accept: */*
> x-country:china
>
< HTTP/1.1 200 OK
< Date: Wed, 04 Aug 2021 15:07:51 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 15
<
country: china
* Connection #0 to ho
```